/*
 * Copyright 2006 Brian Watkins
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */


package bw.util;

import java.io.*;
import java.util.regex.*; 

public class HtmlCheck
{
    private Pattern _httpPattern = null;

    public HtmlCheck ()
    {
        _httpPattern = Pattern.compile( " (https?://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]) " );
        //_httpPattern = Pattern.compile( "\\b(([\\w-]+://?|www[.])[^\\s()<>]+(?:\\([\\w\\d]+\\)|([^[:punct:]\\s]|/)))" );
    }

    public String operate ( String src )
    {
        int index = 0;

        if ( src == null ) { return ""; }

        src = src.trim();

        try
        {
            Tag tag = new Tag( null, -1 );
            while ( ( tag = findNextTag( src, index ) ).tag != null )
            {
                TagData td = isGoodTag( src, tag.tag, tag.startIndex );
                int endStartTag = td.end;

                if ( !td.ok )
                {
                    if ( td.error == -3 )
                    {
                        src = insertInto( src, ">", td.end );
                        endStartTag = td.end;
                    }
                    else if ( td.error == -2 )
                    {
                        src += "\">";
                        endStartTag = src.length() - 1;
                    }
                    else if ( td.error == -1 )
                    {
                        src += ">";
                        endStartTag = src.length() - 1;
                    }
                }

                if ( !hasEndTag( src, tag.tag, endStartTag ) )
                {
                    if ( tag.isSingle() )
                    {
                        //if there are more tags then just make this
                        //one into a single tag, ie <br /> 
                        src = insertInto( src, " /", endStartTag );
                        endStartTag = endStartTag + 2;
                    }
                    else
                    {
                        //add a closing tag, either before the next tag
                        //or at the end
                        String endTag = ( "</" + tag.tag + ">" );
                        int nt = src.indexOf( "<", endStartTag );
                        if ( nt > -1 )
                        {
                            src = insertInto( src, endTag, nt );
                            endStartTag = nt + 3 + tag.tag.length();
                        }
                        else
                        {
                            src += endTag;
                        }
                    }
                }

                index = endStartTag;
            }

            //replace any links that aren't already clickable
            StringBuffer sb = new StringBuffer();
            Matcher matcher = _httpPattern.matcher( " " + src + " " );
            while ( matcher.find() ) 
            {
                matcher.appendReplacement( sb, " <a href=\"" + matcher.group( 1 ) + "\">" + 
                                           matcher.group( 1 ) + "</a> " );
            }
            matcher.appendTail( sb );
            
            src = sb.toString().trim();
        }
        catch ( Exception ex )
        {
            ex.printStackTrace();
        }

        return src;
    }

    private String insertInto ( String s, String t, int ind )
    {
        String a = s.substring( 0, ind );
        String b = s.substring( ind );
        return ( a + t + b );
    }

    private Tag findNextTag ( String src, int start ) throws IOException
    {
        String tag = null;

        int tagStart = start;
        while ( ( tagStart = src.indexOf( "<", tagStart ) ) > -1 )
        {
            if ( src.charAt( tagStart + 1 ) != '/' )
            {
                break;
            }
            tagStart = tagStart + 1;
        }

        if ( tagStart > -1 )
        {
            StringReader reader = new StringReader( src.substring( tagStart + 1 ) );
            int c = -1;
            while ( ( c = reader.read() ) != -1 )
            {
                char ch = (char) c;
                if ( ch == ' ' || ch == '>' )
                {
                    break;
                }

                if ( tag == null ) { tag = ""; }

                tag += ch;
            }
        }

        return ( new Tag( tag, tagStart ) );
    }

    private TagData isGoodTag ( String src, String tag, int start ) throws IOException
    {
        TagData td = new TagData();

        boolean inQuote = false;
        td.end = start + 1;

        int firstSpace = -1;

        StringReader reader = new StringReader( src.substring( start + 1 ) );
        int c = -1;
        while ( ( c = reader.read() ) != -1 )
        {
            char ch = (char) c;

            if ( ch == '"' && !inQuote )
            {
                inQuote = true;
            }
            else if ( ch == '"' && inQuote )
            {
                inQuote = false;
            }
            
            if ( ch == ' ' && firstSpace == -1 )
            {
                firstSpace = td.end;
            }

            if ( ch == '>' && !inQuote )
            {
                td.ok = true;
                break;
            }

            if ( ch == '<' )
            {
                td.ok = false;
                td.error = -3;
                if ( firstSpace > -1 ) 
                {
                    td.end = firstSpace;
                }
                return td;
            }

            td.end++;
        }

        if ( !td.ok && inQuote )
        {
            td.error = -2;
        }
        else if ( !td.ok && !inQuote )
        {
            td.error = -1;
        }

        return td;
    }

    private boolean hasEndTag ( String src, String tag, int start )
    {
        return ( src.substring( 0, start ).endsWith( "/" ) || 
                 ( src.indexOf( "</" + tag + ">", start ) > -1 ) );
    }

    class Tag
    {
        public int startIndex = -1;
        public String tag = null;

        public Tag ( String t, int si )
        {
            startIndex = si;
            tag = t;
        }

        public boolean isSingle ()
        {
            boolean isSingle = false;
            String ltag = tag.toLowerCase();

            //these are the only tags I can
            //think of that would commonly be used
            //without an end tag. can add to this list
            //if needed
            if ( ltag.equals( "br" ) ||
                 ltag.equals( "p" ) ||
                 ltag.equals( "li" ) )
            {
                isSingle = true;
            }

            return isSingle;
        }

    }

    class TagData
    {
        public boolean ok = false;
        public int error = 0;
        public int end = -1;
    }

    public static void main ( String [] args )
    {
        String src = args[0];

        HtmlCheck check = new HtmlCheck();
        String result = check.operate( src );

        System.out.println( "Result:" );
        System.out.println( result );

        System.exit( 0 );
    }
}